/*********************
 *      INCLUDES
 *********************/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdlib.h>
#include <libudev.h>
#include "mouse.h"
#include "common.h"
// #include "lv_types.h"
// #include "lv_indev_private.h"
// #include "lv_display_private.h"

#if LV_USE_LINUX_FBDEV

extern bool HDMI_CONNECT_FALG;
extern bool SYSTEM_RUNNING_FALG;
static bool USB_MOUSE_CONNECT_FALG = false;

#include "./lib/lvgl/lvgl/src/misc/lv_types.h"
#include "./lib/lvgl/lvgl/src/indev/lv_indev_private.h"
#include "./lib/lvgl/lvgl/src/display/lv_display_private.h"



/**********************
 *  STATIC VARIABLES
 **********************/
static int evdev_fd = -1;
static int evdev_root_x;
static int evdev_root_y;
static int evdev_button;

static int evdev_key_val;

#define MAX_DEVICES 64
#define MOUSE_EVENT_KEY BTN_LEFT
#define MOUSE_EVENT_TYPE EV_KEY

/**********************
 *  STATIC PROTOTYPES
 **********************/
static int map(int x, int in_min, int in_max, int out_min, int out_max);

// 检查设备是否支持指定的事件类型和事件码
static int check_event_support(int fd, unsigned int event_type, unsigned int event_code) {
    unsigned char evbit[EV_MAX / 8 + 1] = {0};
    unsigned char keybit[KEY_MAX / 8 + 1] = {0};

    // 获取设备支持的事件类型
    if (ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) {
        reemoon_error ("EVIOCGBIT");
        return 0;
    }
    // 检查设备是否支持指定的事件类型
    if (!(evbit[event_type / 8] & (1 << (event_type % 8)))) {
        return 0;
    }
    // 获取设备支持的按键事件码
    if (ioctl(fd, EVIOCGBIT(event_type, sizeof(keybit)), keybit) < 0) {
        reemoon_error("EVIOCGBIT");
        return 0;
    }
    // 检查设备是否支持指定的事件码
    if (!(keybit[event_code / 8] & (1 << (event_code % 8)))) {
        return 0;
    }
    return 1;
}

static char mouse_dev_path[64];
static char *find_event_mouse(void) 
{
    int fd;
    int i;

    // 遍历 /dev/input/eventX 设备节点
    for (i = 0; i < MAX_DEVICES; i++) 
    {
        snprintf(mouse_dev_path, sizeof(mouse_dev_path), "/dev/input/event%d", i);
        fd = open(mouse_dev_path, O_RDONLY | O_NONBLOCK);
        if (fd < 0) {
            continue;
        }

        // 检查设备是否支持鼠标左键事件

        int has_left_button = check_event_support(fd, EV_KEY, BTN_LEFT);
        int has_relative_movement_x = check_event_support(fd, EV_REL, REL_X);
        int has_relative_movement_y = check_event_support(fd, EV_REL, REL_Y);

        if (check_event_support(fd, MOUSE_EVENT_TYPE, MOUSE_EVENT_KEY) && has_left_button && (has_relative_movement_x || has_relative_movement_y)) 
        {
            USB_MOUSE_CONNECT_FALG = true;
            reemoon_info ("Found mouse device: %s\n", mouse_dev_path);
            close(fd);
            return mouse_dev_path;
        }

        close(fd);
    }
    USB_MOUSE_CONNECT_FALG = false;
    return NULL;
}

/**
 * Initialize the evdev interface
 * reconfigure the device file for evdev
 * @return true: the device file set complete
 *         false: the device file doesn't exist current system
 */
bool mouse_evdev_init(void)
{ 
#if USE_BSD_EVDEV
     evdev_fd = open(dev_name, O_RDWR | O_NOCTTY);
#else
    // 查找鼠标
    evdev_fd = open(find_event_mouse(), O_RDWR | O_NOCTTY | O_NDELAY);
    // usb鼠标不存在
    if (USB_MOUSE_CONNECT_FALG == false)
    {
        reemoon_info ("Usb Mouse device nod fond!\n");
        // 使用触摸屏
        evdev_fd = open(MOUSE_TOUCH_EVDEV, O_RDWR | O_NOCTTY | O_NDELAY);
        if (evdev_fd == -1)
        {
            reemoon_warn ("Usb Mouse and Touch panel device nod fond!\n");
            return false;
        }
    }
#endif

#if USE_BSD_EVDEV
     fcntl(evdev_fd, F_SETFL, O_NONBLOCK);
#else
     fcntl(evdev_fd, F_SETFL, O_ASYNC | O_NONBLOCK);
#endif

     evdev_root_x = 0;
     evdev_root_y = 0;
     evdev_key_val = 0;
     evdev_button = LV_INDEV_STATE_REL;

     return true;
}

void mouse_evdev_deinit(void)
{
     if( evdev_fd ) {
        close(evdev_fd);
     }
    reemoon_info("mouse deinit\n");
}
/**
 * Get the current position and state of the evdev
 * @param data store the evdev data here
 */
void mouse_evdev_read(lv_indev_t * drv, lv_indev_data_t * data)
{
    struct input_event in;

    while(read(evdev_fd, &in, sizeof(struct input_event)) > 0) {
        if(in.type == EV_REL) {
            if(in.code == REL_X)
                #if EVDEV_SWAP_AXES
                    evdev_root_y += in.value;
                #else
                    evdev_root_x += in.value;
                #endif
            else if(in.code == REL_Y)
                #if EVDEV_SWAP_AXES
                    evdev_root_x += in.value;
                #else
                    evdev_root_y += in.value;
                #endif
        } else if(in.type == EV_ABS) {
            if(in.code == ABS_X)
                #if EVDEV_SWAP_AXES
                    evdev_root_y = in.value;
                #else
                    evdev_root_x = in.value;
                #endif
            else if(in.code == ABS_Y)
                #if EVDEV_SWAP_AXES
                    evdev_root_x = in.value;
                #else
                    evdev_root_y = in.value;
                #endif
            else if(in.code == ABS_MT_POSITION_X)
                                #if EVDEV_SWAP_AXES
                                        evdev_root_y = in.value;
                                #else
                                        evdev_root_x = in.value;
                                #endif
            else if(in.code == ABS_MT_POSITION_Y)
                                #if EVDEV_SWAP_AXES
                                        evdev_root_x = in.value;
                                #else
                                        evdev_root_y = in.value;
                                #endif
            else if(in.code == ABS_MT_TRACKING_ID) {
                                if(in.value == -1)
                                    evdev_button = LV_INDEV_STATE_REL;
                                else if(in.value == 0)
                                    evdev_button = LV_INDEV_STATE_PR;
            }
        } else if(in.type == EV_KEY) {
            if(in.code == BTN_MOUSE || in.code == BTN_TOUCH) {
                if(in.value == 0)
                    evdev_button = LV_INDEV_STATE_REL;
                else if(in.value == 1)
                    evdev_button = LV_INDEV_STATE_PR;
            } else if(drv->type == LV_INDEV_TYPE_KEYPAD) {
#if USE_XKB
                data->key = xkb_process_key(in.code, in.value != 0);
#else
                switch(in.code) {
                    case KEY_BACKSPACE:
                        data->key = LV_KEY_BACKSPACE;
                        break;
                    case KEY_ENTER:
                        data->key = LV_KEY_ENTER;
                        break;
                    case KEY_PREVIOUS:
                        data->key = LV_KEY_PREV;
                        break;
                    case KEY_NEXT:
                        data->key = LV_KEY_NEXT;
                        break;
                    case KEY_UP:
                        data->key = LV_KEY_UP;
                        break;
                    case KEY_LEFT:
                        data->key = LV_KEY_LEFT;
                        break;
                    case KEY_RIGHT:
                        data->key = LV_KEY_RIGHT;
                        break;
                    case KEY_DOWN:
                        data->key = LV_KEY_DOWN;
                        break;
                    case KEY_TAB:
                        data->key = LV_KEY_NEXT;
                        break;
                    default:
                        data->key = 0;
                        break;
                }
#endif /* USE_XKB */
                if (data->key != 0) {
                    /* Only record button state when actual output is produced to prevent widgets from refreshing */
                    data->state = (in.value) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
                }
                evdev_key_val = data->key;
                evdev_button = data->state;
                return;
            }
        }
    }

    if(drv->type == LV_INDEV_TYPE_KEYPAD) {
        /* No data retrieved */
        data->key = evdev_key_val;
        data->state = evdev_button;
        return;
    }
    if(drv->type != LV_INDEV_TYPE_POINTER)
        return ;
    /*Store the collected data*/

    lv_coord_t x = evdev_root_x;
    lv_coord_t y = evdev_root_y;

    lv_coord_t hor_res = drv->disp->hor_res;
    lv_coord_t ver_res = drv->disp->ver_res;
    lv_display_rotation_t rotation = lv_display_get_rotation(drv->disp);
    // HDMI 已连接 但是没有插入鼠标的情况下
    if (HDMI_CONNECT_FALG)
    {
        if (USB_MOUSE_CONNECT_FALG == false) // 使用触摸屏控制
        {
            switch (rotation) 
            {
                case LV_DISP_ROTATION_0:
                    x = hor_res - ((float)evdev_root_y*1.0 / (float)TOUCH_MAX_POINT_Y*1.0)*hor_res ;
                    y = ((float)evdev_root_x*1.0 / (float)TOUCH_MAX_POINT_X*1.0)*ver_res ;
                    reemoon_ignore ("evdev_root_x: %d, evdev_root_y: %d\n", evdev_root_x, evdev_root_y);
                    reemoon_ignore ("x: %d, y: %d\n", x, y);
                    break;
                case LV_DISP_ROTATION_90:
                    x = ((float)evdev_root_y*1.0 / (float)TOUCH_MAX_POINT_Y*1.0)*hor_res ;
                    y = ver_res - ((float)evdev_root_x*1.0 / (float)TOUCH_MAX_POINT_X*1.0)*ver_res ;
                    reemoon_ignore ("evdev_root_x: %d, evdev_root_y: %d\n", evdev_root_x, evdev_root_y);
                    reemoon_ignore ("x: %d, y: %d\n", x, y);
                    break;
                case LV_DISP_ROTATION_180:
                    x = hor_res - ((float)evdev_root_y*1.0 / (float)TOUCH_MAX_POINT_Y*1.0)*hor_res ;
                    y = ((float)evdev_root_x*1.0 / (float)TOUCH_MAX_POINT_X*1.0)*ver_res ;
                    reemoon_ignore ("evdev_root_x: %d, evdev_root_y: %d\n", evdev_root_x, evdev_root_y);
                    reemoon_ignore ("x: %d, y: %d\n", x, y);
                    break;
                case LV_DISP_ROTATION_270:
                    x = ((float)evdev_root_y*1.0 / (float)TOUCH_MAX_POINT_Y*1.0)*hor_res ;
                    y = ver_res - ((float)evdev_root_x*1.0 / (float)TOUCH_MAX_POINT_X*1.0)*ver_res ;
                    reemoon_ignore ("evdev_root_x: %d, evdev_root_y: %d\n", evdev_root_x, evdev_root_y);
                    reemoon_ignore ("x: %d, y: %d\n", x, y);
                    break;
            }
        }
    }
    else // 没有接HDMI
    {
        if (USB_MOUSE_CONNECT_FALG == true) // 鼠标控制
        {
            switch (rotation) 
            {
                case LV_DISP_ROTATION_0:
                    break;
                case LV_DISP_ROTATION_90:
                    x = evdev_root_y;
                    y = hor_res - evdev_root_x - 1;
                    break;
                case LV_DISP_ROTATION_180:
                    x = hor_res - evdev_root_x - 1;
                    y = ver_res - evdev_root_y - 1;
                    break;
                case LV_DISP_ROTATION_270:
                    x = ver_res - evdev_root_y - 1;
                    y = evdev_root_x;
                    break;
            }
        }
    }

#if EVDEV_CALIBRATE
    data->point.x = map(x, EVDEV_HOR_MIN, EVDEV_HOR_MAX, 0, hor_res);
    data->point.y = map(y, EVDEV_VER_MIN, EVDEV_VER_MAX, 0, ver_res);
#else
    data->point.x = x;
    data->point.y = y;
#endif

    data->state = evdev_button;

    if(data->point.x < 0)
      data->point.x = 0;
    if(data->point.y < 0)
      data->point.y = 0;
    if(data->point.x >= hor_res)
      data->point.x = hor_res - 1;
    if(data->point.y >= ver_res)
      data->point.y = ver_res - 1;
    return ;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static int map(int x, int in_min, int in_max, int out_min, int out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

/************************************************************************* 热拔插 *************************************************************************/

// 判断设备是否为鼠标
int is_mouse(const char *devnode) {
    int fd = open(devnode, O_RDONLY | O_NONBLOCK);
    if (fd < 0) {
        return 0;
    }

    int has_left_button = check_event_support(fd, EV_KEY, BTN_LEFT);
    int has_relative_movement_x = check_event_support(fd, EV_REL, REL_X);
    int has_relative_movement_y = check_event_support(fd, EV_REL, REL_Y);

    close(fd);

    return has_left_button && (has_relative_movement_x || has_relative_movement_y);
}

// 定义鼠标插入和拔出事件处理函数
void *handle_mouse_events(void *arg) 
{
    struct udev *udev;
    struct udev_monitor *mon;
    int fd;

    // 初始化 udev 上下文
    udev = udev_new();
    if (!udev) {
        perror("Failed to create udev context");
        return NULL;
    }

    // 创建一个 udev 监控器，监听输入子系统的设备事件
    mon = udev_monitor_new_from_netlink(udev, "udev");
    if (!mon) {
        perror("Failed to create udev monitor");
        udev_unref(udev);
        return NULL;
    }

    // 过滤输入子系统的设备事件
    udev_monitor_filter_add_match_subsystem_devtype(mon, "input", NULL);
    udev_monitor_enable_receiving(mon);

    // 获取监控器的文件描述符
    fd = udev_monitor_get_fd(mon);

    reemoon_info ("Monitoring for mouse insertion and removal events...\n");

    while (SYSTEM_RUNNING_FALG) 
    {
        fd_set fds;
        struct timeval tv;
        int ret;

        FD_ZERO(&fds);
        FD_SET(fd, &fds);
        tv.tv_sec = 0;
        tv.tv_usec = 0;

        // 使用 select 函数监听文件描述符
        ret = select(fd + 1, &fds, NULL, NULL, &tv);
        if (ret > 0 && FD_ISSET(fd, &fds)) {
            // 接收设备事件
            struct udev_device *dev = udev_monitor_receive_device(mon);
            if (dev) {
                const char *action = udev_device_get_action(dev);
                const char *devnode = udev_device_get_devnode(dev);

                if (devnode) 
                {
                    if (strcmp(action, "add") == 0 && is_mouse(devnode)) {
                        // memcpy (mouse_dev_path, devnode, strlen(devnode));
                        mouse_evdev_init();
                        printf("Mouse inserted: %s\n", devnode);
                    } 
                    if (strcmp(action, "remove") == 0 && strstr(mouse_dev_path, devnode)) 
                    {
                        USB_MOUSE_CONNECT_FALG = false;
                        mouse_evdev_init();
                        printf("Mouse removed: %s\n", devnode);
                    }
                }
                // 释放设备对象
                udev_device_unref(dev);
            }
        }
    }

    // 释放资源
    udev_monitor_unref(mon);
    udev_unref(udev);
    return NULL;
}

int mouse_check_plughandl(void) 
{
    pthread_t mouse_event_pth_id;
    pthread_create (&mouse_event_pth_id, NULL, handle_mouse_events, NULL);
    return 0;
}
#endif